// ------------------------------------------------------------------------------------
// REVISION LOG ENTRY
//
//	File name  : tkit.js
// Revision By: (C) Copyright 2014 Hiroyuki Tominaga, All Rights Reserved.
//
//	Revised on : 2014-06-11 18:01:43
//	Summary    : https://js-tkit-project.googlecode.com/svn/trunk/tkit.js
//
// ------------------------------------------------------------------------------------
/**
 * class Tkit base.
 */
(function(name_space)
{
	var _DEBUG = 1;
    var tkit, W; {
        W = (window || self);
        W.$ = W.$ || {};
        /* */
        tkit = W[name_space];
        (!tkit) && ( tkit = W[name_space] = {} );
        if (!W.$.noop) {
            W.$.noop = function(){};
        }
    }

	// undefined is not a "undefined"
	// typeof is returns "string" value.
	// ----------------------------------- oop functions. --------------------------------
	/* need for oop extendOverride */
	var _EXTENDED_ = $.generateId(6);

	function extendOverride(_this_clazz, _super) {
		var _this_prototype = _this_clazz.prototype;
		// overwrite _super argment
		_super = _super.prototype;

		for (var p in _super) {
			if(!_this_prototype[p]) // has not property?
				_this_prototype[p] = _super[p];
		}
		return _this_clazz;
	}
	//
	// verify Inheritance.
	//
	//
	function verifyInheritance(
		this_clazz, super_clazz
	) {
		if (this_clazz === null)
			console.trace();
		if( !this_clazz[_EXTENDED_] ) {
			extendOverride(
				this_clazz, super_clazz
			)[_EXTENDED_] = true;
		}
	};

	/*
	[2010-11-25 11:17:32]
		継承するクラスは必ずこの関数を呼んで低レベルの初期化を行います。
		Initialize the class that inherits from the low level of this function must be called.
	*/
	//
	// @param instance		:current instance of object. MUST this
	// @param this_clazz	:
	// @param super_clazz	:
	// @param args			:must be arguments object.
	//
	tkit.initAtom = function initAtom(
		instance,
		this_clazz, super_clazz,
		args
	) {
		//acutual_ns.AfxObject.call(this, this_name);
		if(typeof(super_clazz) === "function")
			super_clazz.apply(instance, args);

		verifyInheritance(
			this_clazz, super_clazz
		);
	}
	// ----------------------------------- oop functions. --------------------------------
	/*
		void XMLHttpRequest.open(
			in AUTF8String method,
			in AUTF8String url,
			[optional] in boolean async,
			[optional] in AString user,
			[optional] in AString password
		);
	*/
    tkit.loadAjax = function (src, callback, async/* optional */) {
        var _xhr = new XMLHttpRequest();
        _xhr.onreadystatechange = function() {
            if (this.readyState == XMLHttpRequest.DONE || this.status == 200) {
	            if (typeof callback === "function") {
	                callback(this.responseText, this);
	            }
            }
        };
        (async === undefined) && (async = false);
        _xhr.open('GET', src, async);
        _xhr.send();
    }

/*
    var protocol = location.protocol,
        hostname = location.hostname,
        exRegex = RegExp(protocol + '//' + hostname),
        YQL = 'http' + (/^https/.test(protocol) ? 's' : '') + '://query.yahooapis.com/v1/public/yql?callback=?',
        query = 'select * from html where url="{URL}" and xpath="*"';
*/
	// http://query.yahooapis.com/v1/public/yql?q=select * from html where url="{URL}" and xpath="*"&callback=cb
	// http://query.yahooapis.com/v1/public/yql?q=select * from html where url="https://js-tkit-project.googlecode.com/svn/trunk/tkit-dragwindow.js" and xpath="*"&callback=cb
	// ----------------------------------- dynamic load class. --------------------------------
	//var tkit_code_base = "https://js-tkit-project.googlecode.com/svn/trunk/";
	var tkit_code_base = "http://jpropedit-gui.sourceforge.jp/tkitloader.php?file=";

	// helper
	function _evalClass(resonseText, xhr) {
		(window || self)["eval"](resonseText);
	}

	tkit.forName = function (className) {
		if (tkit[className] === undefined) {
 			tkit.loadAjax(
				tkit_code_base + "tkit-" + className.toLowerCase() + ".js", _evalClass, false
			);
			/* これはやはりうまくいかない. */
			//$ls(tkit_code_base + "tkit-" + className.toLowerCase() + ".js");
		}
		return tkit[className];
	}
	tkit.exists = function (className) {
		return tkit.forName(className) !== undefined;
	}
	tkit.create = function (className/*, args*/) {
		/*var ctor = tkit.forName(className);
		var object = {};
		ctor.apply(object, Array.prototype.slice.call(arguments, 1));
		object.prototype = ctor.prototype;
		return object;*/
		// Object.create は匿名 class を作る.
		// よって, object instanceof Object しか成立しない.
		// しかし, constructor に作成元の function object を設定してやると
		// object instanceof ctor が成立するようになる.
		// この case で, Object.create を使うことの意義は,
		// function.apply を call することによる可変長 arguments の適用だろう.
		// new ctor とすると, arguments の展開が出来ない.
		var ctor = tkit.forName( Array.prototype.shift.call(arguments) );
		var object = Object.create(ctor.prototype);
		ctor.apply(object, arguments);
		object.constructor = ctor
		return object;
	}
	// ----------------------------------- dynamic load class. --------------------------------


	// -------------------------------------------------------------------------------
	tkit.getRandomColor = function getRandomColor(rgb)
	{
		var c;
		if(rgb) {
			c = "rgb(" +
				Math.floor(Math.random() * 256) + ", " +
				Math.floor(Math.random() * 256) + ", " +
				Math.floor(Math.random() * 256) + ")";
		}
		else {
			c = "#";
			for(var i = 0; i < 3; i++) {
				var hex = Math.floor(Math.random() * 256).toString(16);
				c += hex.length === 2? hex: "0" + hex;
			}
		}
		return c;
	};

	// -------------------------------------------------------------------------------
	//
	//
	// -------------------------------------------------------------------------------
	tkit._bind_listener = $.noop;
	/**
	 * bindClasses 実行時に call される.
	 */
	tkit.setBindEventListener = function setBindEventListener(l) {
		tkit._bind_listener = (typeof l === "function")? l: $.noop;
	};
    /**
     * この function 完了後, setBindEventListener で set された listener は削除される.
     * @param cfg_id json を記述した element id.(innerText property を使用する
     *  json format:
     *  ------------------------------------------------------------
     *  {
     *      "<bind element id>": {
     *          <config for bind class>
     *      }
     *  }
     *  @param callback class が構築されるたびに instance が渡される.(必要ないときは function 以外の値を設定
     *                  function callback(tkit_class) {}
     *  @param dynamic_load class が load されていないとき(存在しないとき)
     *                      remote server から script の load を試みます.
     *                      存在しない class, または何らかの例外が発生した場合は null を返す.
     */
	tkit.bindClasses = function bindClasses(cfg_id, callback, dynamic_load) {
		var tkit_elements = $("[data-tkit-class]");
		if (typeof callback !== "function")
			callback = tkit._bind_listener;

		var classes = {}, json; { // cfg_id を指定しない場合, 空の json object となる.
			json = document.getElementById(cfg_id);
			if (json === null)
				json = {};
			else
				json = JSON.parse(json.innerHTML); //innerText
		}
		if (dynamic_load) {
			tkit_elements.each(
				function(i, e) {
					var fqdn = e.getAttribute("data-tkit-class").split(":");
					var config  = (fqdn.length === 2)? fqdn[1]: json;
					fqdn = fqdn[0];
					try {
						if (tkit.exists(fqdn)) {
							var tkit_class = tkit.create(fqdn, e);
							_bindClassesHelper(e, classes, tkit_class, config);
							callback(tkit_class);
						}
					} catch (e) {
						console.log(e);
					}
				}
			);
		} else {
			tkit_elements.each(
				function(i, e) {
					var fqdn = e.getAttribute("data-tkit-class").split(":");
					var config  = (fqdn.length === 2)? fqdn[1]: json;
					fqdn = fqdn[0];
					try {
						var tkit_class = new tkit[fqdn](e);
						_bindClassesHelper(e, classes, tkit_class, config);
						callback(tkit_class);
					} catch (e) {
						console.log(e);
					}
				}
			);
		}
		delete tkit._bind_listener;

		return classes;
		//console.log("remove listener", delete tkit._bind_listener); // 2014-06-13 10:03:43 true, ok.
	};

	function _bindClassesHelper(e, classes, tkit_class, config) {
		classes[e.id] = tkit_class;
		_DEBUG && console.log(tkit_class);
		tkit_class.setOptions(config);
		//tkit_class.install();
	}

	/**
	 * base class である XWidget を直接継承する場合,
	 * この shortcut function を利用可能.
	 *
	 * @param instance   MUST be "this".
	 * @param this_clazz MUST be "this" constructor.
	 * @param args       MUST be "arguments"
	 *
	 *    Tkit.extendBase(this, arguments);
	 *
	 * @see Tkit#initAtom(instance, this_clazz, super_clazz, arguments)
	 */
	// note: 2014-06-25 15:56:49
	// 元は, extendBase(instance, args) だったが, instance.constructor を
	// this_clazz として使用するとうまくいかなかった...
	tkit.extendBase = function extendBase(instance, this_clazz, args) {
		var super_clazz = tkit.XWidget;
		super_clazz.apply(instance, args);
		verifyInheritance( this_clazz, super_clazz );
	}
	/**
	 * class Tkit.XWidget
	 *
	 * Tkit package base class.
	 */
	tkit.XWidget = function XWidget(element) {
		//this.attach(element); // これは error となった.
		this._e = typeof element === "string"? $dom(element): element;
	};
	tkit.XWidget.qname = name_space + ".XWidget";
	tkit.XWidget.prototype = {
		// to jQuery object.
		$: function () { return $(this._e); },
		attach: function (element) {
			this._e = typeof element === "string"? $dom(element): element;
			return this;
		},
        release: function () {
        	// If needed, override...
            return this;
        },
        // 簡易 check function.
        is: function(element) {
            var e = this._e;
            if (!e) return false;
            return e.id === element || e === element;
        },

		install: $.noop,
		updateOptions: $.noop,
		/*
		* html element に plain text として設定情報を記述する.
		* 構文に関しては, class の実装ごとに異なる.
		*/
		setOptions: function setOptions(config) {
			if (typeof config === "string") { // means config element id
				var e = $dom(config);
				if (e !== null) {
					var src = e.getAttribute("data-src");
					if (src !== null && src.length > 0) {
						var _self = this;
						function load_done(text) {
							_self.updateOptions( JSON.parse(text) );
						};
						tkit.loadAjax(src, load_done);
					}
					else
						this.updateOptions( JSON.parse(e.innerText) );

					return;
				} else { // json string?
					try {
						config = JSON.parse(config);
					} catch (e) {
						console.log(e);
					}
				}
			}
			 // json object.
			if ($.isPlainObject(config))
				this.updateOptions(config);
		}
	};

})("Tkit");	// (function()
